﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace IDotNet.MapLibs.Web
{
    public class BMapLib
    {
        #region GetDistance 计算两点距离
        protected double lw(double a, double b, double c)
        {
            a = Math.Max(a, b);
            a = Math.Min(a, c);
            return a;
        }

        protected double ew(double a, double b, double c)
        {
            while (a > c)
                a -= c - b;
            while (a < b)
                a += c - b;
            return a;
        }

        protected double oi(double a)
        {
            return Math.PI * a / 180;
        }

        protected double Td(double a, double b, double c, double d)
        {
            return 6370996.81 * Math.Acos(Math.Sin(c) * Math.Sin(d) + Math.Cos(c) * Math.Cos(d) * Math.Cos(b - a));
        }

        protected double Wv(PointModel a, PointModel b)
        {
            a.lng = ew(a.lng, -180, 180);
            a.lat = lw(a.lat, -74, 74);
            b.lng = ew(b.lng, -180, 180);
            b.lat = lw(b.lat, -74, 74);
            return Td(oi(a.lng), oi(b.lng), oi(a.lat), oi(b.lat));
        }

        public double GetDistance(PointModel a, PointModel b)
        {
            var c = Wv(a, b);
            return c;
        }
        #endregion

        ///// <summary>
        ///// 计算一个点是否在多边形里
        ///// </summary>
        ///// <param name="pt">坐标</param>
        ///// <param name="poly">多边形坐标数组</param>
        ///// <returns></returns>
        //public bool IsInsidePolygon(PointModel pt, List<PointModel> poly)
        //{
        //    var c = false;
        //    int i, j, l;
        //    for (i = -1, l = poly.Count, j = l - 1; ++i < l; j = i)
        //    {
        //        if (((poly[i].lat <= pt.lat && pt.lat < poly[j].lat) || (poly[j].lat <= pt.lat && pt.lat < poly[i].lat))
        //            && (pt.lng < (poly[j].lng - poly[i].lng) * (pt.lat - poly[i].lat) / (poly[j].lat - poly[i].lat) + poly[i].lng))
        //        {
        //            c = !c;
        //        }
        //    }
        //    return c;
        //}

        /// <summary>
        /// 计算一个点是否在多边形里
        /// </summary>
        /// <param name="p">坐标</param>
        /// <param name="pts">多边形坐标数组</param>
        /// <returns></returns>
        public bool IsInsidePolygon(PointModel p, List<PointModel> pts)
        {
            //下述代码来源：http://paulbourke.net/geometry/insidepoly/，进行了部分修改
            //基本思想是利用射线法，计算射线与多边形各边的交点，如果是偶数，则点在多边形外，否则
            //在多边形内。还会考虑一些特殊情况，如点在多边形顶点上，点在多边形边上等特殊情况。

            var N = pts.Count;
            var boundOrVertex = true; //如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
            var intersectCount = 0;//cross points count of x 
            var precision = 2e-10; //浮点类型计算时候与0比较时候的容差
            PointModel p1, p2;//neighbour bound vertices

            p1 = pts[0];//left vertex        
            for (var i = 1; i <= N; ++i)
            {//check all rays            
                if (p.Equals(p1))
                {
                    return boundOrVertex;//p is an vertex
                }

                p2 = pts[i % N];//right vertex            
                if (p.lat < Math.Min(p1.lat, p2.lat) || p.lat > Math.Max(p1.lat, p2.lat))
                {//ray is outside of our interests                
                    p1 = p2;
                    continue;//next ray left point
                }

                if (p.lat > Math.Min(p1.lat, p2.lat) && p.lat < Math.Max(p1.lat, p2.lat))
                {//ray is crossing over by the algorithm (common part of)
                    if (p.lng <= Math.Max(p1.lng, p2.lng))
                    {//x is before of ray                    
                        if (p1.lat == p2.lat && p.lng >= Math.Min(p1.lng, p2.lng))
                        {//overlies on a horizontal ray
                            return boundOrVertex;
                        }

                        if (p1.lng == p2.lng)
                        {//ray is vertical                        
                            if (p1.lng == p.lng)
                            {//overlies on a vertical ray
                                return boundOrVertex;
                            }
                            else
                            {//before ray
                                ++intersectCount;
                            }
                        }
                        else
                        {//cross point on the left side                        
                            var xinters = (p.lat - p1.lat) * (p2.lng - p1.lng) / (p2.lat - p1.lat) + p1.lng;//cross point of lng                        
                            if (Math.Abs(p.lng - xinters) < precision)
                            {//overlies on a ray
                                return boundOrVertex;
                            }

                            if (p.lng < xinters)
                            {//before ray
                                ++intersectCount;
                            }
                        }
                    }
                }
                else
                {//special case when ray is crossing through the vertex                
                    if (p.lat == p2.lat && p.lng <= p2.lng)
                    {//p crossing over p2                    
                        var p3 = pts[(i + 1) % N]; //next vertex                    
                        if (p.lat >= Math.Min(p1.lat, p3.lat) && p.lat <= Math.Max(p1.lat, p3.lat))
                        {//p.lat lies between p1.lat & p3.lat
                            ++intersectCount;
                        }
                        else
                        {
                            intersectCount += 2;
                        }
                    }
                }
                p1 = p2;//next ray left point
            }

            if (intersectCount % 2 == 0)
            {//偶数在多边形外
                return false;
            }
            else
            { //奇数在多边形内
                return true;
            }     
        }

    }
}